# 基于随机算法的模版AI
# 此处采取的算法为优先在己方领域下, 己方满则在对方领域随机下

# 参赛队伍的AI要求:
#
# 须写在Player类里
#
# 须实现两个方法:
#
# __init__(self, isFirst, array):
#   -> 初始化
#   -> 参数: isFirst是否先手, 为bool变量, isFirst = True 表示先手
#   -> 参数: array随机序列, 为一个长度等于总回合数的list
#
# output(self, currentRound, board, mode):
#   -> 给出己方的决策(下棋的位置或合并的方向)
#   -> 参数: currentRound当前轮数, 为从0开始的int
#   -> 参数: board棋盘对象
#   -> 参数: mode模式, mode = 'position' 对应位置模式, mode = 'direction' 对应方向模式, 如果为 '_position' 和 '_direction' 表示在对应模式下己方无法给出合法输出
#   -> 返回: 位置模式返回tuple (row, column), row行, 从上到下为0到3的int; column列, 从左到右为0到7的int
#   -> 返回: 方向模式返回direction = 0, 1, 2, 3 对应 上, 下, 左, 右
#   -> 返回: 在己方无法给出合法输出时, 对返回值不作要求
#
# 其余的属性与方法请自行设计
from random import *

class Stack:
    def __init__(self):
        self.items = []

    def isEmpty(self):
        return self.items == []

    def push(self, item):
        self.items.append(item)

    def pop(self):
        return self.items.pop()

    def peek(self):
        return self.items[len(self.items) - 1]

    def size(self):
        return len(self.items)



class Player:
    def __init__(self, isFirst, array):
        # 初始化
        self.isFirst = isFirst
        self.array = array
        self.board = None
        self.best_direction = None
        self.best_position = None
        # 后期优化可以考虑排一个direction和position决策的优先队列，防止best direction或position是None
        # 代码中暂时让best决策为None时选择随机一个方向
        self.depth = 2  #DEPTH
        self.another = None

    def output(self, currentRound, board, mode):  # 由userinterface.py可得，主程序中调用output时传入的board参数为copy完毕的board对象
        self.board = board
        if mode == 'position':  # 给出己方下棋的位置
            '''another = board.getNext(self.isFirst, currentRound)  # 己方的允许落子点
            if another != (): return another  # 己方领地有可以下棋的地方，则优先选择在己方下棋
            # 如果己方领地无可下棋位置，则再考虑是否在对方那里下棋
            available = board.getNone(not self.isFirst)  # 对方的允许落子点
            if not available:   # 整个棋盘已满
                return None
            else:
                from random import choice
                return choice(available)'''
            self.another = self.board.getNext(self.isFirst, currentRound)
            return self.choose_position()
        elif mode == 'direction':  # 给出己方合并的方向
            '''from random import shuffle
            directionList = [0, 1, 2, 3]
            shuffle(directionList)
            for direction in directionList:
                #board.move()返回棋盘是否变化，若为False则是非法行为。
                if board.move(self.isFirst, direction): return direction ''' #如果这个方向是合法方向，就选择这个方向进行合并。
            return self.choose_direction()
        else:  # mode参数为_position或者_direction
            return


    def choose_position(self):  # 带框架的白痴
        self.get_PositionValue(self.depth, -1000000, 1000000, -1, self.isFirst)
        if self.best_position:
            return self.best_position
        # best_position可能为None吗？
        if self.another != ():
            return self.another  # 己方领地有可以下棋的地方，则优先选择在己方下棋
        # 如果己方领地无可下棋位置，则再考虑是否在对方那里下棋
        available = self.board.getNone(not self.isFirst)  # 对方的允许落子点
        if not available:  # 整个棋盘已满
            return None
        else:
            #from random import choice
            return choice(available)


    def choose_direction(self):  # 带框架的白痴
        self.get_DirectionValue(self.depth, -1000000, 1000000, -1, self.isFirst)
        if self.best_direction:
            return self.best_direction
        else:  #因为评估函数没有，故瞎编的value可能导致best_direction为非法方向，这时只能随便选一个合法方向合并。
            #from random import shuffle
            directionList = [0, 1, 2, 3]
            shuffle(directionList)
            for direction in directionList:
                # board.move()返回棋盘是否变化，若为False则是非法行为。
                if self.board.move(self.isFirst, direction):
                    return direction


    def get_BoardValue(self,player):  # 评估函数 待更改 先随便赋值一个
        import random
        value = -random.randint(1,6) * player  # player=-1时为己方，player=1时为对方；己方value为正数，越大越有利。
        #value = -10*player
        return value

    def get_PositionValue(self, depth, alpha, beta, player, isFirst):
        # 运用negamax算法获取这一步决策的value值
        global prev_stack

        if depth == 0:
            return self.get_BoardValue(player)

        positionList = []
        if self.board.getNone(not self.isFirst):
            positionList += [choice(self.board.getNone(not self.isFirst))]
            # 注意：这样构造的positionList过于庞大且漫无目的，后续要改，选择出几个有必要的（比如对方棋盘要进行大数合并时选择在两者中间）
        if self.another:
            positionList.append(self.another)

        for position in positionList:
            if depth == self.depth:  # 意思是处于第一层时要创建一个stack用于回溯上一步时的棋盘状态，用于cancel move
                prev_stack = Stack()
            current_board = self.board.copy()
            self.board.add(self.isFirst if player == -1 else (not self.isFirst), position)  # make move
            prev_stack.push(current_board)

            if isFirst:  # 本层player为先手方，即下一步是另一方选择位置下棋
                value = -self.get_PositionValue(depth - 1, -beta, -alpha, -player, not isFirst)
            else:  # 本层player为后手方，即下一步是另一方选择位置下棋
                value = -self.get_DirectionValue(depth - 1, -beta, -alpha, -player, not isFirst)

            self.board = prev_stack.pop()  # cancel move

            if value >= alpha:
                if depth == self.depth:
                    self.best_position = position
                alpha = value
            if alpha >= beta:
                break
        return alpha


    def get_DirectionValue(self,depth,alpha,beta,player,isFirst):  # player=-1时为己方，player=1时为对方
        # 运用negamax算法获取这一步决策的value值
        global prev_stack
        if depth == 0:
            return self.get_BoardValue(player)

        directionList = [0, 1, 2, 3]
        for direction in directionList:
            if depth == self.depth:  # 意思是处于第一层时要创建一个stack用于回溯上一步时的棋盘状态，用于cancel move
                prev_stack = Stack()
            current_board = self.board.copy()
            if self.board.move(self.isFirst if player == -1 else (not self.isFirst), direction):  # 此合并方向合法时make move
                prev_stack.push(current_board)

                if isFirst:  # 本层player为先手方，即下一步是另一方选择方向进行合并
                    value = -self.get_DirectionValue(depth-1,-beta,-alpha,-player,not isFirst)
                else:  # 本层player为后手方，即下一步是另一方选择位置下棋
                    value = -self.get_PositionValue(depth-1,-beta,-alpha,-player,not isFirst)

                self.board = prev_stack.pop()  # cancel move

                if value >= alpha:
                    if depth == self.depth:
                        self.best_direction = direction
                    alpha = value
                if alpha >= beta:
                    break
        return alpha











